package com.buddy.learn.spring.framework.webmvc.servlet;

import com.buddy.learn.spring.framework.context.BApplicationContext;
import com.buddy.learn.spring.mvc.annotation.BController;
import com.buddy.learn.spring.mvc.annotation.BRequestMapping;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//@sL4J
public class BDispatcherServlet extends HttpServlet {

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
    private BApplicationContext context;

    //保存url和method的关系
    private List<BHandlerMapping> handlerMappings = new ArrayList<>();
    //	private List<HandlerAdapter> handlerAdapters;
    private Map<BHandlerMapping,BHandlerAdapter> handlerAdapters = new HashMap<>();
    private List<BViewResolver> viewResolvers = new ArrayList<>();


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1.通过从request中获取到url去匹配一个HandlerMapping
        BHandlerMapping handler = getHandler(req);
        if (handler == null){
            //404
            processDispatchResult(req,resp,null, new BModelAndView("404"));
            return;

        }
        //2.准备好调用前的参数
        BHandlerAdapter ha = getHandlerAdapter(handler);

        //3.真正的调用方法，返回modelandView，存储了要传的页面上的值和页面模板的名称
        BModelAndView mv = ha.handle(req,resp,handler);

        //这一步才是真正的输出
        processDispatchResult(req,resp,handler,mv);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, BHandlerMapping handler, BModelAndView mv) throws Exception {
        //把ModelAndView变成一个HTML/OutputStream/JSON/Freemark/veolcity
        //ContextType决定
        if (null == mv){
            return;
        }
        //如果modelAndView不为null
        if(this.viewResolvers.isEmpty()){
            return;
        }
        for (BViewResolver viewResolver : viewResolvers) {
            BView view = viewResolver.resolveViewName(mv.getViewName(),null);
            view.render(mv.getModel(),req,resp);
            return;
        }


    }

    private BHandlerAdapter getHandlerAdapter(BHandlerMapping handler) {
        if (this.handlerAdapters.isEmpty()){
            return null;
        }
        BHandlerAdapter ha = this.handlerAdapters.get(handler);
        if (ha.supports(handler)){
            return ha;
        }
        return null;
    }

    private BHandlerMapping getHandler(HttpServletRequest req) throws Exception{
        if (this.handlerMappings.isEmpty()) return null;
        //绝对路径
        String url = req.getRequestURI();
        //处理成相对路径；
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
        for (BHandlerMapping handler : this.handlerMappings) {
            try{
                Matcher matcher = handler.getPattern().matcher(url);
                //如果没有匹配上继续下一个
                if (!matcher.matches()){
                    continue;
                }
                return handler;
            }catch (Exception e){
                e.printStackTrace();
                throw e;
            }

        }
        return null;

    }
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1.初始化ApplicationContext
        context = new BApplicationContext(CONTEXT_CONFIG_LOCATION);
        //2.初始化SpringMVC的九大组件
        initStrategies(context);
    }


    //Spring MVC 的九大组件的初始化
    protected void initStrategies(BApplicationContext context) {
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);
        //TODO handlerMapping
        initHandlerMappings(context);
        //TODO 初始化参数适配器
        initHandlerAdapters(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        //TODO 初始化视图转换器
        initViewResolvers(context);
        //FlashMap 管理器，参数缓存器
        initFlashMapManager(context);
    }

    private void initFlashMapManager(BApplicationContext context) {
    }

    private void initViewResolvers(BApplicationContext context) {
        //拿到模板的存放目录
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        File templateRootDir = new File(templateRootPath);
        String[] templates =  templateRootDir.list();
        for (int i=0;i<templates.length;i++) {
            //这里主要是为了兼容多模板，所有模仿Spring用List保存；
            //简化代码中，使用一个模板就可以搞定
            //只是为了仿真，所以好事搞了一个List
            this.viewResolvers.add(new BViewResolver(templateRoot));
        }

    }

    private void initRequestToViewNameTranslator(BApplicationContext context) {
    }

    private void initHandlerExceptionResolvers(BApplicationContext context) {
    }

    private void initHandlerAdapters(BApplicationContext context) {
        //将request请求变成handler，参数都是String，自动匹配到handler中的形参。

        //可想而知，他要拿到handlerMapping才能干活
        //意味着，有几个handlerMapping就有几个handlerAdapter
        for (BHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new BHandlerAdapter());
        }

    }

    private void initHandlerMappings(BApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames) {

                Object controller = context.getBean(beanName);
                Class<?> clazz = controller.getClass();
                if (!clazz.isAnnotationPresent(BController.class)) {
                    continue;
                }
                //保存类上面的@BRequestMapping("/")
                String baseUrl = "";
                if (clazz.isAnnotationPresent(BRequestMapping.class)) {
                    BRequestMapping requestMapping = clazz.getAnnotation(BRequestMapping.class);
//                baseUrl = requestMapping.value();
                    baseUrl = requestMapping.value()[0];
                }

                //默认获取所有的public方法
                for (Method method : clazz.getMethods()) {
                    if (!method.isAnnotationPresent(BRequestMapping.class)) continue;
                    BRequestMapping requestMapping = method.getAnnotation(BRequestMapping.class);
                    String regex = "/" + baseUrl + "/" + requestMapping.value()[0]
                            .replaceAll("\\*",".*")
                            .replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
                    this.handlerMappings.add(new BHandlerMapping(pattern,controller,method));
                    System.out.println("Mapped:" + regex + "," + method);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void initThemeResolver(BApplicationContext context) {
    }

    private void initLocaleResolver(BApplicationContext context) {
    }

    private void initMultipartResolver(BApplicationContext context) {
    }

}
